﻿using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Threading.Tasks;
using System.Threading;
using Engine;
using Engine.Plot;
using log4net;
using log4net.Config;
using ResearchDemonstration.DAO;
using Microsoft.Win32;
using ResearchDemonstration.Dialogs;
using System.ComponentModel;
using System;
using System.Collections.Generic;
using System.IO;
using ResearchDemonstration.Export;

namespace ResearchDemonstration
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow
    {
        /// <summary>
        /// Logger
        /// </summary>
        private static readonly ILog Log = LogManager.GetLogger(typeof(MainWindow));

        /// <summary>
        /// Cotain reference to cancelatoion token
        /// </summary>
        private CancellationTokenSource _cancellationTokenSource;

        /// <summary>
        /// Used for passing finded value to UI thread
        /// </summary>
        private delegate void DispatchToUIDelegate();

        /// <summary>
        /// Contain display view of finded elements
        /// </summary>
        private readonly ObservableCollection<PlotPoint> _observableCollection;
        
        /// <summary>
        /// Handle database operations
        /// </summary>
        private readonly DatabaseManager _databaseManager;
        
        /// <summary>
        /// Research engine that perform research operation
        /// </summary>
        private ResearchEngine _engine;

        public MainWindow()
        {
            XmlConfigurator.Configure();
            InitializeComponent();
            
            // initialise code here
            m_notifyIcon = new System.Windows.Forms.NotifyIcon();
            m_notifyIcon.BalloonTipText = "The app has been minimised. Click the tray icon to show.";
            m_notifyIcon.BalloonTipTitle = "The App";
            m_notifyIcon.Text = "The App";
            m_notifyIcon.Icon = new System.Drawing.Icon("TheAppIcon.ico");
            m_notifyIcon.Click += m_notifyIcon_Click;

            _observableCollection = new ObservableCollection<PlotPoint>();
            dataGrid.ItemsSource = _observableCollection;
            _databaseManager = new DatabaseManager();
        }


        #region tray icon

        private System.Windows.Forms.NotifyIcon m_notifyIcon;
        
        void OnClose(object sender, CancelEventArgs args)
        {
            m_notifyIcon.Dispose();
            m_notifyIcon = null;
        }

        private WindowState m_storedWindowState = WindowState.Normal;
        
        void OnStateChanged(object sender, EventArgs args)
        {
            if (WindowState == WindowState.Minimized)
            {
                Hide();
                if (m_notifyIcon != null)
                    m_notifyIcon.ShowBalloonTip(2000);
            }
            else
                m_storedWindowState = WindowState;
        }
        void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs args)
        {
            CheckTrayIcon();
        }

        void m_notifyIcon_Click(object sender, EventArgs e)
        {
            Show();
            WindowState = m_storedWindowState;
        }
        void CheckTrayIcon()
        {
            ShowTrayIcon(!IsVisible);
        }

        void ShowTrayIcon(bool show)
        {
            if (m_notifyIcon != null)
                m_notifyIcon.Visible = show;
        }    

        #endregion

        /// <summary>
        /// Start research
        /// </summary>
        private void OnStartResearch(object sender, RoutedEventArgs e)
        {
            Button button = sender as Button;
            button.Click -= OnStartResearch;
            button.Click += OnStopResearch;
            button.Content = "Стоп";
            exportButton.IsEnabled = false;
            int xSize = int.Parse(xPointCountTextBox.Text);
            int ySize = int.Parse(yPointCountTextBox.Text);
            int iterations = int.Parse(numberOfIteractionTextBox.Text);
            _engine = new ResearchEngine( xSize , ySize , iterations );
            _cancellationTokenSource = new CancellationTokenSource();
            _engine.PlotPointFindedEvent += OnPlotPointFinded;
            _engine.EndEvent += (sen, eArgs) => this.Dispatcher.BeginInvoke(new DispatchToUIDelegate(OnStoppedResearch));
            TaskFactory factory = Task.Factory;
            Task task = factory.StartNew(() => _engine.StudyStudingDependanceConductivityOnNumberOfElements(_cancellationTokenSource.Token));
            TaskScheduler.UnobservedTaskException += OnTaskException;
        }

        /// <summary>
        /// When any unhandled errors occured
        /// </summary>
        /// <param name="sender">task on which we have exception</param>
        /// <param name="e">excepton args</param>
        private void OnTaskException(object sender, UnobservedTaskExceptionEventArgs e)
        {
            if (Log.IsErrorEnabled)
            {
                Log.Error("Непредвиденная ошибка при выполнении операции");
            }
        }

        /// <summary>
        /// When we find new research value.
        /// </summary>
        private void OnPlotPointFinded(object sender, PlotPointFindedEventArgs e)
        {
            //we need to redirect this to windows level
            this.Dispatcher.BeginInvoke(new DispatchToUIDelegate(() =>
               {
                   _observableCollection.Add(e.Point);
                   _observableCollection.Add(e.NormPoint);
                   if (_observableCollection.Count > 50) 
                   {
                       //removing seriesPercolationThreashold element
                       _observableCollection.RemoveAt(0);
                       _observableCollection.RemoveAt(0);
                   }
                   _databaseManager.AddRecord(e.Point);
                   _databaseManager.AddRecord(e.NormPoint);
               }));
        }

        /// <summary>
        /// stop research
        /// </summary>
        private void OnStopResearch(object sender, RoutedEventArgs e)
        {
            _cancellationTokenSource.Cancel();
            (sender as Button).IsEnabled = false;
        }

        /// <summary>
        /// Notify UI that we stopped the operation
        /// </summary>
        private void OnStoppedResearch()
        {
            startStopButton.Click += OnStartResearch;
            startStopButton.Click -= OnStopResearch;
            startStopButton.Content = "Пуск";
            startStopButton.IsEnabled = true;
            exportButton.IsEnabled = true;
        }

        /// <summary>
        /// Open dialog ehere user can chose file to download
        /// </summary>
        /// <param name="sender">sender object</param>
        /// <param name="e">event arg</param>
        private void OnLoadLatticeFromFile(object sender, RoutedEventArgs e)
        {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Filter = "save point|*.bin";
            dialog.Multiselect = false;
            if(dialog.ShowDialog()== true)
            {
                startStopButton.IsEnabled = false;
                TaskFactory factory = Task.Factory;
                OperationProgressDialogBox operation = new OperationProgressDialogBox();
                operation.Show();
                //execute on separate thread
                Task task = factory.StartNew( () => 
                {
                    _engine.DeserializeLatticeFromFile(dialog.FileName);
                    this.Dispatcher.Invoke(new DispatchToUIDelegate( () => startStopButton.IsEnabled = true ));
                });
            }
        }

        /// <summary>
        /// Open dialog where user can specify file to which download
        /// </summary>
        /// <param name="sender">sender object</param>
        /// <param name="e">event args</param>
        private void OnSaveLatticeToFile(object sender,RoutedEventArgs e)
        {
            SaveFileDialog dialog = new SaveFileDialog();
            dialog.Filter = "save point|*.bin";
            dialog.OverwritePrompt = true;
            dialog.FileName = "LatticeId-" + _engine.CurrentLattice.Id + "-x-" + System.Math.Round((double) _engine.CurrentLattice.XBonds, 2) + ".bin";            
            if(dialog.ShowDialog() == true)
            {
                Task.Factory.StartNew( () => _engine.SerializeLatticeToFile(dialog.SafeFileName) );    
            }
        }

        /// <summary>
        /// Process calculated data from database
        /// </summary>
        private void OnDataProccessing(object sender, RoutedEventArgs e) 
        {
            //gets calculated data from database
            IDictionary<int,IList<PlotPoint>> data = _databaseManager.getAllData();
            //then we will find average data and finally save it to text file
            DataAnalizator analizator = new DataAnalizator(data);
            IList<PlotPoint> analizedData = analizator.Analize();
            //then we print final values to main table
            _observableCollection.Clear();
            foreach(var point in analizedData)
            {
                _observableCollection.Add(point);
            }
        }
        
        /// <summary>
        /// Export all date in tabel to text file
        /// </summary>
        /// <param name="e">event args</param>
        /// <param name="sender">sender button</param>
        private void OnExportTableDataToFile(object sender, RoutedEventArgs e) 
        {
            SaveFileDialog ofd = new SaveFileDialog();
            ofd.Filter = "text files|*.txt";
            ofd.OverwritePrompt = true;
            if(ofd.ShowDialog()  == true)
            {
                string filePath = ofd.SafeFileName; 
                Stream stream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);
                StreamWriter writer = new StreamWriter(stream);
                foreach(var point in _observableCollection)
                {
                    //export data to excel readable format
                    writer.WriteLine(point.X + " " + point.Conductance);
                }
                writer.Close();
                stream.Close();
            }
        }

        /// <summary>
        /// Initialize operation of saving data to exel file.
        /// </summary>
        /// <param name="sender">sender</param>
        /// <param name="e">event</param>
        public void OnDataProcessExport(object sender, RoutedEventArgs e) 
        {
            SaveFileDialog sfd = new SaveFileDialog();
            sfd.Filter = "exel 97-2003 files|*.xls";
            sfd.OverwritePrompt = true;
            if(sfd.ShowDialog() == true)
            {
                string filePath = sfd.SafeFileName;
                Task.Factory.StartNew(() =>
                {
                    try
                    {
                        //gets calculated data from database
                        IDictionary<int, IList<PlotPoint>> data = _databaseManager.getAllData();
                        //then we will find average data and finally save it to text file
                        DataAnalizator analizator = new DataAnalizator(data);
                        IList<PlotPoint> analizedData = analizator.Analize();
                        DataAnalizeExelReport reporter = new DataAnalizeExelReport();
                        reporter.ExportToExelFile(filePath, analizedData);
                    }
                    catch(Exception exc)
                    {
                        if (Log.IsErrorEnabled) 
                        {
                            Log.Error("problem when export in exel", exc);
                        }
                    }
                });
            }
        }
    }
}
